package org.raftkv.raft.pb.utils;

import org.raftkv.raft.exceptions.RaftException;
import org.raftkv.raft.pb.exceptions.IntOverflowRaftException;
import org.raftkv.raft.pb.exceptions.InvalidLengthRaftException;
import org.raftkv.raft.pb.exceptions.UnexpectedEOFException;
import org.raftkv.raft.pb.exceptions.UnexpectedEndOfGroupRaftException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class PbUtils {

    private static final Logger LOGGER = LoggerFactory.getLogger(PbUtils.class);

    public static long sovRaft(long x) {
        long ans = 0;
        while (true) {
            ++ans;
            x >>>= 7;
            if (x == 0)
                return ans;
        }
    }

    public static int encodeVarintRaft(byte[] data, int offset, long v) {
        offset -= sovRaft(v);
        int base = offset;

        while (v >= (1 << 7)) {
            data[offset] = (byte) (v & 0x7f | 0x80);
            v >>= 7;
            offset++;
        }
        data[offset] = (byte) v;
        return base;
    }

    public static int skipRaft(byte[] data) {
        int l = data.length;
        int index = 0;
        int depth = 0;

        while (index < l) {
            long wire = 0;
            for (long shift = 0; ; shift += 7) {
                if (shift >= 64) {
                    throw new IntOverflowRaftException();
                }

                if (index >= l) {
                    throw new UnexpectedEOFException();
                }

                byte b = data[index];
                index++;
                wire |= (b & 0x7F) << shift;
                if (b < 0x80) {
                    break;
                }
            }
            int wireType = (int) (wire & 0x7);
            switch (wireType) {
                case 0:
                    for (long shift = 0; ; shift += 7) {
                        if (shift >= 64) {
                            throw new IntOverflowRaftException();
                        }

                        if (index >= l) {
                            throw new UnexpectedEOFException();
                        }

                        byte b = data[index];
                        index++;
                        if (data[index - 1] < 0x80) {
                            break;
                        }
                    }
                    break;
                case 1:
                    index += 8;
                    break;
                case 2:
                    int length = 0;
                    for (long shift = 0; ; shift += 7) {
                        if (shift >= 64) {
                            throw new IntOverflowRaftException();
                        }

                        if (index >= l) {
                            throw new UnexpectedEOFException();
                        }

                        byte b = data[index];
                        index++;
                        length |= (b & 0x7f) << shift;
                        if (b < 0x80) {
                            break;
                        }
                    }
                    if (length < 0) {
                        throw new InvalidLengthRaftException();
                    }
                    index += length;
                    break;
                case 3:
                    depth++;
                    break;
                case 4:
                    if (depth == 0) {
                        throw new UnexpectedEndOfGroupRaftException();
                    }
                    depth--;
                    break;
                case 5:
                    index += 4;
                    break;
                default:
                    String errMsg = String.format("illegal wireType %d", wireType);
                    LOGGER.error(errMsg);
                    throw new RaftException(errMsg);
            }
            if (index < 0) {
                throw new InvalidLengthRaftException();
            }
            if (depth == 0) {
                return index;
            }
        }
        throw new UnexpectedEOFException();
    }

}
